{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = pd.read_csv('./dataset/HR.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>satisfaction_level</th>\n",
       "      <th>last_evaluation</th>\n",
       "      <th>number_project</th>\n",
       "      <th>average_montly_hours</th>\n",
       "      <th>time_spend_company</th>\n",
       "      <th>Work_accident</th>\n",
       "      <th>left</th>\n",
       "      <th>promotion_last_5years</th>\n",
       "      <th>part</th>\n",
       "      <th>salary</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.38</td>\n",
       "      <td>0.53</td>\n",
       "      <td>2</td>\n",
       "      <td>157</td>\n",
       "      <td>3</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>sales</td>\n",
       "      <td>low</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.80</td>\n",
       "      <td>0.86</td>\n",
       "      <td>5</td>\n",
       "      <td>262</td>\n",
       "      <td>6</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>sales</td>\n",
       "      <td>medium</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0.11</td>\n",
       "      <td>0.88</td>\n",
       "      <td>7</td>\n",
       "      <td>272</td>\n",
       "      <td>4</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>sales</td>\n",
       "      <td>medium</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0.72</td>\n",
       "      <td>0.87</td>\n",
       "      <td>5</td>\n",
       "      <td>223</td>\n",
       "      <td>5</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>sales</td>\n",
       "      <td>low</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0.37</td>\n",
       "      <td>0.52</td>\n",
       "      <td>2</td>\n",
       "      <td>159</td>\n",
       "      <td>3</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>sales</td>\n",
       "      <td>low</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   satisfaction_level  last_evaluation  number_project  average_montly_hours  \\\n",
       "0                0.38             0.53               2                   157   \n",
       "1                0.80             0.86               5                   262   \n",
       "2                0.11             0.88               7                   272   \n",
       "3                0.72             0.87               5                   223   \n",
       "4                0.37             0.52               2                   159   \n",
       "\n",
       "   time_spend_company  Work_accident  left  promotion_last_5years   part  \\\n",
       "0                   3              0     1                      0  sales   \n",
       "1                   6              0     1                      0  sales   \n",
       "2                   4              0     1                      0  sales   \n",
       "3                   5              0     1                      0  sales   \n",
       "4                   3              0     1                      0  sales   \n",
       "\n",
       "   salary  \n",
       "0     low  \n",
       "1  medium  \n",
       "2  medium  \n",
       "3     low  \n",
       "4     low  "
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'pandas.core.frame.DataFrame'>\n",
      "RangeIndex: 14999 entries, 0 to 14998\n",
      "Data columns (total 10 columns):\n",
      " #   Column                 Non-Null Count  Dtype  \n",
      "---  ------                 --------------  -----  \n",
      " 0   satisfaction_level     14999 non-null  float64\n",
      " 1   last_evaluation        14999 non-null  float64\n",
      " 2   number_project         14999 non-null  int64  \n",
      " 3   average_montly_hours   14999 non-null  int64  \n",
      " 4   time_spend_company     14999 non-null  int64  \n",
      " 5   Work_accident          14999 non-null  int64  \n",
      " 6   left                   14999 non-null  int64  \n",
      " 7   promotion_last_5years  14999 non-null  int64  \n",
      " 8   part                   14999 non-null  object \n",
      " 9   salary                 14999 non-null  object \n",
      "dtypes: float64(2), int64(6), object(2)\n",
      "memory usage: 1.1+ MB\n"
     ]
    }
   ],
   "source": [
    "data.info()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['sales', 'accounting', 'hr', 'technical', 'support', 'management',\n",
       "       'IT', 'product_mng', 'marketing', 'RandD'], dtype=object)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.part.unique()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['low', 'medium', 'high'], dtype=object)"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.salary.unique()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = data.join(pd.get_dummies(data.part)).join(pd.get_dummies(data.salary))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>satisfaction_level</th>\n",
       "      <th>last_evaluation</th>\n",
       "      <th>number_project</th>\n",
       "      <th>average_montly_hours</th>\n",
       "      <th>time_spend_company</th>\n",
       "      <th>Work_accident</th>\n",
       "      <th>left</th>\n",
       "      <th>promotion_last_5years</th>\n",
       "      <th>part</th>\n",
       "      <th>salary</th>\n",
       "      <th>...</th>\n",
       "      <th>hr</th>\n",
       "      <th>management</th>\n",
       "      <th>marketing</th>\n",
       "      <th>product_mng</th>\n",
       "      <th>sales</th>\n",
       "      <th>support</th>\n",
       "      <th>technical</th>\n",
       "      <th>high</th>\n",
       "      <th>low</th>\n",
       "      <th>medium</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0.38</td>\n",
       "      <td>0.53</td>\n",
       "      <td>2</td>\n",
       "      <td>157</td>\n",
       "      <td>3</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>sales</td>\n",
       "      <td>low</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0.80</td>\n",
       "      <td>0.86</td>\n",
       "      <td>5</td>\n",
       "      <td>262</td>\n",
       "      <td>6</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>sales</td>\n",
       "      <td>medium</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0.11</td>\n",
       "      <td>0.88</td>\n",
       "      <td>7</td>\n",
       "      <td>272</td>\n",
       "      <td>4</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>sales</td>\n",
       "      <td>medium</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0.72</td>\n",
       "      <td>0.87</td>\n",
       "      <td>5</td>\n",
       "      <td>223</td>\n",
       "      <td>5</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>sales</td>\n",
       "      <td>low</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0.37</td>\n",
       "      <td>0.52</td>\n",
       "      <td>2</td>\n",
       "      <td>159</td>\n",
       "      <td>3</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>sales</td>\n",
       "      <td>low</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>5 rows × 23 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "   satisfaction_level  last_evaluation  number_project  average_montly_hours  \\\n",
       "0                0.38             0.53               2                   157   \n",
       "1                0.80             0.86               5                   262   \n",
       "2                0.11             0.88               7                   272   \n",
       "3                0.72             0.87               5                   223   \n",
       "4                0.37             0.52               2                   159   \n",
       "\n",
       "   time_spend_company  Work_accident  left  promotion_last_5years   part  \\\n",
       "0                   3              0     1                      0  sales   \n",
       "1                   6              0     1                      0  sales   \n",
       "2                   4              0     1                      0  sales   \n",
       "3                   5              0     1                      0  sales   \n",
       "4                   3              0     1                      0  sales   \n",
       "\n",
       "   salary  ...  hr  management  marketing  product_mng  sales  support  \\\n",
       "0     low  ...   0           0          0            0      1        0   \n",
       "1  medium  ...   0           0          0            0      1        0   \n",
       "2  medium  ...   0           0          0            0      1        0   \n",
       "3     low  ...   0           0          0            0      1        0   \n",
       "4     low  ...   0           0          0            0      1        0   \n",
       "\n",
       "   technical  high  low  medium  \n",
       "0          0     0    1       0  \n",
       "1          0     0    0       1  \n",
       "2          0     0    0       1  \n",
       "3          0     0    1       0  \n",
       "4          0     0    1       0  \n",
       "\n",
       "[5 rows x 23 columns]"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "data.drop(columns=['part', 'salary'], inplace=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    11428\n",
       "1     3571\n",
       "Name: left, dtype: int64"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.left.value_counts()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.7619174611640777"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "11428/(11428+3571)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "Y_data = data.left.values.reshape(-1, 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "Y = torch.from_numpy(Y_data).type(torch.FloatTensor)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[1.],\n",
       "        [1.],\n",
       "        [1.],\n",
       "        ...,\n",
       "        [1.],\n",
       "        [1.],\n",
       "        [1.]])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([14999, 1])"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Y.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['satisfaction_level',\n",
       " 'last_evaluation',\n",
       " 'number_project',\n",
       " 'average_montly_hours',\n",
       " 'time_spend_company',\n",
       " 'Work_accident',\n",
       " 'promotion_last_5years',\n",
       " 'IT',\n",
       " 'RandD',\n",
       " 'accounting',\n",
       " 'hr',\n",
       " 'management',\n",
       " 'marketing',\n",
       " 'product_mng',\n",
       " 'sales',\n",
       " 'support',\n",
       " 'technical',\n",
       " 'high',\n",
       " 'low',\n",
       " 'medium']"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[c for c in data.columns if c != 'left']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_data = data[[c for c in data.columns if c != 'left']].values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = torch.from_numpy(X_data).type(torch.FloatTensor)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([14999, 20])"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 创建模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "from torch import nn"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Logistic(nn.Module):\n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "        self.lin_1 = nn.Linear(20, 64)\n",
    "        self.lin_2 = nn.Linear(64, 64)\n",
    "        self.lin_3 = nn.Linear(64, 1)\n",
    "        self.activate = nn.ReLU()\n",
    "        self.sigmoid = nn.Sigmoid()\n",
    "    def forward(self, input):\n",
    "        x = self.lin_1(input)\n",
    "        x = self.activate(x)\n",
    "        x = self.lin_2(x)\n",
    "        x = self.activate(x)\n",
    "        x = self.lin_3(x)\n",
    "        x = self.sigmoid(x)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 我们将定义一个小函数来创建我们的模型和优化器，以便将来可以重用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "lr = 0.0001"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_model():\n",
    "    model = Logistic()\n",
    "    return model, torch.optim.Adam(model.parameters(), lr=lr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义损失函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "loss_fn = nn.BCELoss()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "model, opt = get_model()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "14999"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "batch = 64\n",
    "no_of_batches = len(data)//batch\n",
    "epochs = 100"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch: 99     loss: tensor(0.5176, grad_fn=<BinaryCrossEntropyBackward>)\n"
     ]
    }
   ],
   "source": [
    "for epoch in range(epochs):\n",
    "    for i in range(no_of_batches):\n",
    "        start = i*batch\n",
    "        end = start + batch\n",
    "        x = X[start: end]\n",
    "        y = Y[start: end]\n",
    "        y_pred = model(x)\n",
    "        loss = loss_fn(y_pred, y)\n",
    "        opt.zero_grad()\n",
    "        loss.backward()\n",
    "        opt.step()\n",
    "print('epoch:', epoch, '   ', 'loss:', loss_fn(model(X), Y))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 使用dataset进行重构"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "PyTorch有一个抽象的Dataset类。Dataset可以是任何具有__len__函数和__getitem__作为对其进行索引的方法的函数。 本教程将通过示例将自定义HRDataset类创建为的Dataset的子类。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "PyTorch的TensorDataset 是一个包装张量的Dataset。通过定义索引的长度和方式，这也为我们提供了沿张量的第一维进行迭代，索引和切片的方法。这将使我们在训练的同一行中更容易访问自变量和因变量。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "from torch.utils.data import TensorDataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "HRdataset = TensorDataset(X, Y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "model, opt = get_model()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch: 99     loss: tensor(0.5395, grad_fn=<BinaryCrossEntropyBackward>)\n"
     ]
    }
   ],
   "source": [
    "for epoch in range(epochs):\n",
    "    for i in range(no_of_batches):\n",
    "        x, y = HRdataset[i * batch: i * batch + batch]\n",
    "        y_pred = model(x)\n",
    "        loss = loss_fn(y_pred, y)\n",
    "        opt.zero_grad()\n",
    "        loss.backward()\n",
    "        opt.step()\n",
    "print('epoch:', epoch, '   ', 'loss:', loss_fn(model(X), Y))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 使用DataLoader进行重构"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pytorch DataLoader负责管理批次。\n",
    "\n",
    "DataLoader从Dataset创建。\n",
    "\n",
    "DataLoader使遍历批次变得更容易。DataLoader会自动为我们提供每个小批量。\n",
    "\n",
    "无需使用 HRdataset[i * batch: i * batch + batch]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "from torch.utils.data import DataLoader\n",
    "\n",
    "HR_ds = TensorDataset(X, Y)\n",
    "HR_dl = DataLoader(HR_ds, batch_size=batch)"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "现在，我们的循环更加简洁了，因为（xb，yb）是从数据加载器自动加载的：\n",
    "\n",
    "for x,y in HR_dl:\n",
    "    pred = model(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "model, opt = get_model()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch: 99     loss: tensor(0.5204, grad_fn=<BinaryCrossEntropyBackward>)\n"
     ]
    }
   ],
   "source": [
    "for epoch in range(epochs):\n",
    "    for x, y in HR_dl:\n",
    "        y_pred = model(x)\n",
    "        loss = loss_fn(y_pred, y)\n",
    "        opt.zero_grad()\n",
    "        loss.backward()\n",
    "        opt.step()\n",
    "print('epoch:', epoch, '   ', 'loss:', loss_fn(model(X), Y))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 添加验证"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "前面我们只是试图建立一个合理的训练循环以用于我们的训练数据。实际上，您始终还应该具有一个验证集，以识别您是否过度拟合。\n",
    "\n",
    "训练数据的乱序（shuffle）对于防止批次与过度拟合之间的相关性很重要。另一方面，无论我们是否乱序验证集，验证损失都是相同的。由于shufle需要额外的开销，因此shuffle验证数据没有任何意义。\n",
    "\n",
    "我们将为验证集使用批大小，该批大小是训练集的两倍。这是因为验证集不需要反向传播，因此占用的内存更少（不需要存储梯度）。我们利用这一优势来使用更大的批量，并更快地计算损失。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "!pip install sklearn -i https://pypi.douban.com/simple/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.model_selection import train_test_split"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_x, test_x, train_y, test_y = train_test_split(X_data, Y_data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((11249, 20), (11249, 1), (3750, 20), (3750, 1))"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "train_x.shape, train_y.shape, test_x.shape, test_y.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_x = torch.from_numpy(train_x).type(torch.FloatTensor)\n",
    "test_x = torch.from_numpy(test_x).type(torch.FloatTensor)\n",
    "train_y = torch.from_numpy(train_y).type(torch.FloatTensor)\n",
    "test_y = torch.from_numpy(test_y).type(torch.FloatTensor)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_ds = TensorDataset(train_x, train_y)\n",
    "train_dl = DataLoader(train_ds, batch_size=batch, shuffle=True)\n",
    "\n",
    "valid_ds = TensorDataset(test_x, test_y)\n",
    "valid_dl = DataLoader(valid_ds, batch_size=batch * 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 定义计算正确率函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [],
   "source": [
    "def accuracy(out, yb):\n",
    "    preds = (out>0.5).type(torch.IntTensor)\n",
    "    return (preds == yb).float().mean()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "model.train()在训练之前调用代表训练模式\n",
    "\n",
    "model.eval() 推理之前进行调用代表推理模式\n",
    "\n",
    "不同的模式仅会在使用nn.BatchNorm2d ，nn.Dropout等层时以确保这些不同阶段的行为正确。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [],
   "source": [
    "epochs = 500"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 tensor(0.5753) 0.7549068\n",
      "50 tensor(0.3225) 0.8585389\n",
      "100 tensor(0.2648) 0.898835\n",
      "150 tensor(0.2400) 0.91091007\n",
      "200 tensor(0.2246) 0.9196683\n",
      "250 tensor(0.2125) 0.92663103\n",
      "300 tensor(0.2198) 0.92376643\n",
      "350 tensor(0.2107) 0.92506856\n",
      "400 tensor(0.2276) 0.9196683\n",
      "450 tensor(0.2129) 0.923506\n",
      "500 tensor(0.2130) 0.92663103\n"
     ]
    }
   ],
   "source": [
    "model, opt = get_model()\n",
    "\n",
    "for epoch in range(epochs+1):\n",
    "    model.train()\n",
    "    for xb, yb in train_dl:\n",
    "        pred = model(xb)\n",
    "        loss = loss_fn(pred, yb)\n",
    "\n",
    "        loss.backward()\n",
    "        opt.step()\n",
    "        opt.zero_grad()\n",
    "    if epoch%50==0:\n",
    "        model.eval()\n",
    "        with torch.no_grad():\n",
    "            valid_loss = sum(loss_fn(model(xb), yb) for xb, yb in valid_dl)\n",
    "            acc_mean = np.mean([accuracy(model(xb), yb) for xb, yb in valid_dl])\n",
    "        print(epoch, valid_loss / len(valid_dl), acc_mean)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 优化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Logistic(nn.Module):\n",
    "    def __init__(self):\n",
    "        super().__init__()\n",
    "        self.lin_1 = nn.Linear(20, 64)\n",
    "        self.lin_2 = nn.Linear(64, 64)\n",
    "        self.lin_3 = nn.Linear(64, 64)\n",
    "        self.lin_4 = nn.Linear(64, 1)\n",
    "        self.activate = nn.ReLU()\n",
    "        self.sigmoid = nn.Sigmoid()\n",
    "    def forward(self, input):\n",
    "        x = self.lin_1(input)\n",
    "        x = self.activate(x)\n",
    "        x = self.lin_2(x)\n",
    "        x = self.activate(x)\n",
    "        x = self.lin_3(x)\n",
    "        x = self.activate(x)\n",
    "        x = self.lin_4(x)\n",
    "        x = self.sigmoid(x)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 tensor(0.5742) 0.76320267 0.7549068\n",
      "50 tensor(0.2785) 0.88764817 0.89180374\n",
      "100 tensor(0.2118) 0.9235763 0.9269874\n",
      "150 tensor(0.1965) 0.92992663 0.9289748\n",
      "200 tensor(0.1926) 0.93254286 0.93236023\n",
      "250 tensor(0.1852) 0.93928283 0.9362665\n",
      "300 tensor(0.1778) 0.9432779 0.93965185\n",
      "350 tensor(0.1758) 0.94339377 0.93939143\n",
      "400 tensor(0.2052) 0.9267651 0.91820174\n",
      "450 tensor(0.1701) 0.94368726 0.939131\n",
      "500 tensor(0.1655) 0.9481607 0.93955594\n"
     ]
    }
   ],
   "source": [
    "model, opt = get_model()\n",
    "\n",
    "acc_val = []\n",
    "acc_train = []\n",
    "\n",
    "for epoch in range(epochs+1):\n",
    "    model.train()\n",
    "    for xb, yb in train_dl:\n",
    "        pred = model(xb)\n",
    "        loss = loss_fn(pred, yb)\n",
    "\n",
    "        loss.backward()\n",
    "        opt.step()\n",
    "        opt.zero_grad()\n",
    "    if epoch%50==0:\n",
    "        model.eval()\n",
    "        with torch.no_grad():\n",
    "            valid_loss = sum(loss_fn(model(xb), yb) for xb, yb in valid_dl)\n",
    "            acc_mean_train = np.mean([accuracy(model(xb), yb) for xb, yb in train_dl])\n",
    "            acc_mean_val = np.mean([accuracy(model(xb), yb) for xb, yb in valid_dl])\n",
    "        acc_train.append(acc_mean_train)\n",
    "        acc_val.append(acc_mean_val)\n",
    "        print(epoch, valid_loss / len(valid_dl), acc_mean_train, acc_mean_val)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 创建fit（）和get_data（）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [],
   "source": [
    "def loss_batch(model, loss_func, xb, yb, opt=None):\n",
    "    loss = loss_func(model(xb), yb)\n",
    "\n",
    "    if opt is not None:\n",
    "        loss.backward()\n",
    "        opt.step()\n",
    "        opt.zero_grad()\n",
    "\n",
    "    return loss.item(), len(xb)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "def fit(epochs, model, loss_func, opt, train_dl, valid_dl):\n",
    "    for epoch in range(epochs):\n",
    "        model.train()\n",
    "        for xb, yb in train_dl:\n",
    "            loss_batch(model, loss_func, xb, yb, opt)\n",
    "\n",
    "        model.eval()\n",
    "        with torch.no_grad():\n",
    "            losses, nums = zip(\n",
    "                *[loss_batch(model, loss_func, xb, yb) for xb, yb in valid_dl]\n",
    "            )\n",
    "        val_loss = np.sum(np.multiply(losses, nums)) / np.sum(nums)\n",
    "\n",
    "        print(epoch, val_loss)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_data(train_ds, valid_ds, bs):\n",
    "    return (\n",
    "        DataLoader(train_ds, batch_size=bs, shuffle=True),\n",
    "        DataLoader(valid_ds, batch_size=bs * 2),\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 现在，我们获取数据加载器和拟合模型的整个过程可以在3行代码中运行："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0 0.5466923921585083\n",
      "1 0.5437768836975098\n",
      "2 0.5458150820096334\n",
      "3 0.5379680653254191\n",
      "4 0.5520771965344747\n",
      "5 0.5333976115544637\n",
      "6 0.5301197660128275\n",
      "7 0.5239578514099121\n",
      "8 0.5194751345634461\n",
      "9 0.5135513541857402\n",
      "10 0.5126034033457438\n",
      "11 0.5128153882344564\n",
      "12 0.49411540430386863\n",
      "13 0.4861029712994893\n",
      "14 0.48530904811223347\n",
      "15 0.47241837673187254\n",
      "16 0.48379859307607015\n",
      "17 0.45810359698931374\n",
      "18 0.451055920346578\n",
      "19 0.44228789970080057\n",
      "20 0.43615612886746724\n",
      "21 0.42766183932622276\n",
      "22 0.4213903223514557\n",
      "23 0.4172084908803304\n",
      "24 0.4100531432946523\n",
      "25 0.403511372423172\n",
      "26 0.3971054848988851\n",
      "27 0.3925201852162679\n",
      "28 0.3868228485584259\n",
      "29 0.3867359171072642\n",
      "30 0.37675094564755757\n",
      "31 0.3722356454372406\n",
      "32 0.3681845734278361\n",
      "33 0.3643391172091166\n",
      "34 0.375385142993927\n",
      "35 0.3803580154577891\n",
      "36 0.3540113541762034\n",
      "37 0.3471658375898997\n",
      "38 0.3483211691220601\n",
      "39 0.3409267953713735\n",
      "40 0.3389663805802663\n",
      "41 0.3331018171946208\n",
      "42 0.33961131242116294\n",
      "43 0.32884247654279075\n",
      "44 0.3257884572505951\n",
      "45 0.32702152598698936\n",
      "46 0.3233153475125631\n",
      "47 0.3181049926598867\n",
      "48 0.31367013959884643\n",
      "49 0.3116594916979472\n",
      "50 0.3127574569384257\n",
      "51 0.3117335654099782\n",
      "52 0.3135116806983948\n",
      "53 0.3024268948237101\n",
      "54 0.30008214608828226\n",
      "55 0.29781802503267923\n",
      "56 0.295339426279068\n",
      "57 0.29438308706283567\n",
      "58 0.29470105900764465\n",
      "59 0.2950596555709839\n",
      "60 0.2910015578746796\n",
      "61 0.28614500681559246\n",
      "62 0.2839122186024984\n",
      "63 0.28479854329427085\n",
      "64 0.28113062960306806\n",
      "65 0.2799656581401825\n",
      "66 0.28622450954119366\n",
      "67 0.27563644851048785\n",
      "68 0.2751559883594513\n",
      "69 0.2791439280986786\n",
      "70 0.2814995235443115\n",
      "71 0.26917571449279787\n",
      "72 0.2683711039543152\n",
      "73 0.2665656768163045\n",
      "74 0.2638190315882365\n",
      "75 0.2642504180510839\n",
      "76 0.2662091735283534\n",
      "77 0.26105292274157205\n",
      "78 0.26769063319365183\n",
      "79 0.2776716009616852\n",
      "80 0.25652159039974215\n",
      "81 0.2694076891263326\n",
      "82 0.2541972472747167\n",
      "83 0.25367126063505807\n",
      "84 0.25749096571604413\n",
      "85 0.24970360470612843\n",
      "86 0.2485950039625168\n",
      "87 0.24806051561037698\n",
      "88 0.2629275055567423\n",
      "89 0.24739874091148376\n",
      "90 0.255422341688474\n",
      "91 0.2469493301232656\n",
      "92 0.24713748529752094\n",
      "93 0.2454066114505132\n",
      "94 0.24185793486436208\n",
      "95 0.24159429549376168\n",
      "96 0.24417256344954172\n",
      "97 0.2388138730128606\n",
      "98 0.24304283109505972\n",
      "99 0.2423896360238393\n"
     ]
    }
   ],
   "source": [
    "train_dl, valid_dl = get_data(train_ds, valid_ds, batch)\n",
    "model, opt = get_model()\n",
    "fit(epochs, model, loss_fn, opt, train_dl, valid_dl)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "tch_gpu",
   "language": "python",
   "name": "tch2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
